#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <time.h>

#include "progress_bar.h"

static int judge_param(struct progress_bar *progress_param); 

int progress_bar_init(struct progress_bar *progress_bar, func_t *draw)
{
    if (!progress_bar) {
        printf("INIT ERROR\n");
        return FAULT;
    }

    draw[INSIDE] =  printf_inside; 
    draw[OUTSIDE] = printf_outside;

    progress_bar->length = INIT_LENGTH;
    progress_bar->speed = INIT_SPEED;
    progress_bar->outside_color = INIT_OUTSIDE_COLOR;
    progress_bar->inside_color = INIT_INSIDE_COLOR;
    progress_bar->x_direction = INIT_X_DIRECTION;
    progress_bar->y_direction = INIT_Y_DIRECTION;

    return TURE;
}

int progress_bar_run(struct progress_bar *progress_bar, func_t *draw)
{
    int option = OUTSIDE;
    int color = progress_bar->outside_color;
    
    system("clear");
    HIDE_CURSOR();

    while (1) {
        if (option == OUTSIDE) {
            color = progress_bar->outside_color;
        } else if (option == INSIDE) {
            color = progress_bar->inside_color;
        } else {
            break;
        }
        
        draw[option](progress_bar->length, color, progress_bar->speed,
                    progress_bar->x_direction, progress_bar->y_direction);

        option++;
    }

    SHOW_CURSOR();

    return TURE;
}

void progress_bar_destroy(void)
{
    printf(NONE);
    printf("\n");
    SHOW_CURSOR();
    system("clear");
}

/*
 *@brief   通过传入参数打印不同颜色和大小的外部框架
 *@param   length:进度条长度
 *         inside_color:外框的颜色
 *         speed:打印进度条的速度
 *         x_direction, y_direction:进度条位置
 **/
void printf_outside(int length, int outside_color, int speed, int x_direction, int y_direction)
{   
    int count;

    for (count = 0; count < length + 2; count++) {
        /* 打印上边框 */
        MOVETO(x_direction, y_direction + count);
        printf(BACK_COLOR, outside_color);
        printf(" ");

        /* 打印下边框 */
        MOVETO(x_direction + 2, y_direction + count);
        printf(BACK_COLOR, outside_color);
        printf(" ");
    }

    /* 打印左边框 */
    MOVETO(x_direction + 1, y_direction);
    printf(BACK_COLOR, outside_color);
    printf(" ");

    /* 打印右边框 */
    MOVETO(x_direction + 1, y_direction + length + 1);
    printf(BACK_COLOR, outside_color);
    printf(" ");

    printf(NONE);
    printf("\n");
    printf("\n");
    printf("速度：%d毫秒/次\n", (speed / 1000));

    return;
}

/*
 *@brief   通过传入的参数打印不同颜色、长度、速度的进度条
 *@param   length:进度条长度
 *         inside_color:进度条颜色
 *         speed:打印进度条的速度
 *         x_direction, y_direction:进度条位置
 **/
void printf_inside(int length, int inside_color, int speed, int x_direction, int y_direction)
{    
    int count = 0, percent = 0, compare;
    char *str = "-/|\\";
    
    while (1) {
        compare = (length * count) / 100;

        for (; percent < compare; percent++) {
            MOVETO(x_direction + 1, y_direction + 1 + percent);
            printf(BACK_COLOR, inside_color);
            printf(" ");
        }

        MOVETO(x_direction + 1, y_direction + length + 4);
        printf(NONE);
        printf("%d%% [%c]", count, str[count % 4]);
        usleep(speed);
        fflush(stdout);

        if (count == 100) {
            MOVETO(x_direction + 1, y_direction + length + 4);
            printf("%d%% [COMPLETE]", count);
            break;
        }

        count++;
    }

    printf(NONE);
    printf("\n");
    printf("\n");
    printf("\n");

    return;
}

/*
 *@brief   从terminal得到进度条的参数，参数错误则直接结束程序
 *         更改命令行参数可以改变进度条属性
 *         例如: ./game -l20 -o47 -x3 -y3 -i42 
 *@param   progress_param:存放进度条参数的指针
 *         param_argc:终端参数个数
 *         param_argv:终端参数
 * */
void set_terminal_param(int param_argc, char *param_argv[],
                        struct progress_bar *progress_param)
{
    int opt, ret;

    if (!progress_param) {
        printf("SET_PARAM ERROR!\n");
        exit(FAULT);
    }

    while ((opt = getopt(param_argc, param_argv, "l:s:o:i:x:y:")) != -1) {
        switch (opt) {
            case 'l':
                if (atoi(optarg) < 0 || atoi(optarg) > 256) {
                    printf("输入长度错误！\n");
                    exit(FAULT);
                }
                else
                   progress_param->length = atoi(optarg);
                break;

            case 's':
                if (atoi(optarg) < 0 || atoi(optarg) > 256) {
                    printf("输入速度错误！\n");
                    exit(FAULT);
                }
                else
                   progress_param->speed = atoi(optarg) * 1000; 
                break;

            case 'o':
                if (atoi(optarg) < 0 || atoi(optarg) > 256) {
                    printf("输入外框颜色错误！\n");
                    exit(FAULT);
                }
                else
                   progress_param->outside_color = atoi(optarg); 
                break;

            case 'i':
                if (atoi(optarg) < 0 || atoi(optarg) > 256) {
                    printf("输入进度条颜色错误！\n");
                    exit(FAULT);
                }
                else
                   progress_param->inside_color = atoi(optarg); 
                break;

            case 'x':
                if (atoi(optarg) < 0 || atoi(optarg) > 256) {
                    printf("输入x坐标错误\n");
                    exit(FAULT);
                }
                else
                   progress_param->x_direction = atoi(optarg); 
                break;

            case 'y':
                if (atoi(optarg) < 0 || atoi(optarg) > 256) {
                    printf("输入y坐标错误\n");
                    exit(FAULT);
                }
                else
                   progress_param->y_direction = atoi(optarg); 
                break;
            
            case '?':
                printf("无效参数选项，有效选项为 -l -x -y -o -s -i 并加上参数\n");
                exit(FAULT);

            default:
                break;
        }
    }

    ret = judge_param(progress_param);

    if (ret == FAULT)
        exit(FAULT);
}

/*
 *@brief  检查终端初始化的参数是否超出范围
 *@param  progress_param:存放进度条参数的指针
 *@return 成功返回 TURE
 *        失败返回 FAULT
 * */
int judge_param(struct progress_bar *progress_param)
{
    if (!progress_param) {
        printf("JUDGE_PARAM ERROR!\n");
        return FAULT;
    }

    if (progress_param->length > MAX_LENGTH ||
        progress_param->length < MIN_LENGTH) {
        printf("length范围： 1 -- 150\n");
        return FAULT;
    } else if (progress_param->speed > MAX_SPEED ||
              progress_param->speed < MIN_SPEED) {
        printf("speed范围： 1 -- 200\n");
        return FAULT;
    } else if (progress_param->outside_color > MAX_COLOR ||
              progress_param->outside_color < MIN_COLOR ||
              progress_param->inside_color > MAX_COLOR ||
              progress_param->inside_color < MIN_COLOR) {
        printf("color范围：41 -- 47\n");
        return FAULT;
    } else if (progress_param->x_direction > MAX_DIRECTION ||
              progress_param->x_direction < MIN_DIRECTION ||
              progress_param->y_direction > MAX_DIRECTION ||
              progress_param->y_direction < MIN_DIRECTION) {
        printf("x_direction 、y_direrction范围：1 -- 40\n");
        return FAULT;
    } else {             
        return TURE;
    }
}
